package lab.craft;

/**
 * 简单语法解析器
 * 解析简单的表达式、变量声明和初始化语句、赋值语句
 * 语法规则：
 * program -> intDeclare | expressionStatement | assignmentStatement
 * intDeclare -> 'int' id ( = additive ) ';'
 * expressionStatement -> additive ';
 * additive -> multiplicative ((+ | -) multiplicative) *
 * multiplicative -> primary ((* | /) primary) *
 * primary -> IntLiteral | Id | (additive)
 */


public class SimpleParser {
    public static void main(String[] args) {
        SimpleParser parser = new SimpleParser();
        String script = null;
        ASTNode tree = null;

        try {
            script = "int age = 34 + 3; age = 30; age + 10*30;";
            System.out.println("parsing: "+script);
            tree = parser.parse(script);
            parser.dumpAST(tree, "");
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 错误语法
        try {
            script = "2+3_;";
            System.out.println("parsing: " + script);
            tree = parser.parse(script);
            parser.dumpAST(tree, "");
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 异常语法
        try {
            script = "2+3*;";
            System.out.println("parsing: " + script);
            tree = parser.parse(script);
            parser.dumpAST(tree, "");
        }catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 解析脚本
     * @param script 脚本
     * @return 根节点
     * @throws Exception
     */
    public ASTNode parse(String script) throws Exception {
        SimpleLexer lexer = new SimpleLexer();
        TokenReader tokenReader = lexer.tokenize(script);
        return prog(tokenReader);
    }

    private SimpleASTNode prog(TokenReader tokenReader) throws Exception {
        SimpleASTNode node = new SimpleASTNode(ASTNodeType.Program, "pwc");
        while (tokenReader.peek() != null) {
            // 整型声明
            SimpleASTNode child = intDeclare(tokenReader);
            if (child == null) {
                // 表达式
                child = expressionStatement(tokenReader);
            }
            if (child == null) {
                // 赋值
                child = assignmentStatement(tokenReader);
            }
            if (child != null) {
                node.addChild(child);
            } else  {
                throw new Exception("unknown statement");
            }
        }
        return node;
    }

    /**
     * 表达式语句，表达式后面跟个分号
     * @param tokenReader token 列表
     * @return 节点
     * @throws Exception 错误信息
     */
    private SimpleASTNode expressionStatement(TokenReader tokenReader) throws Exception {
        int pos = tokenReader.getPosition();
        SimpleASTNode node = additive(tokenReader);
        if (node != null) {
            Token token = tokenReader.peek();
            if (token != null && token.getType() == TokenType.SemiColon) {
                tokenReader.read();
            } else {
                node = null;
                // 回溯，不是表达式语句，尝试赋值语句等
                tokenReader.setPosition(pos);
            }
        }
        return node;
    }

    /**
     * 赋值语句，如 age = 20;
     * @param tokenReader token 列表
     * @return 节点
     * @throws Exception 错误信息
     */
    private SimpleASTNode assignmentStatement(TokenReader tokenReader) throws Exception {
        SimpleASTNode node = null;
        Token token = tokenReader.peek();
        if (token != null && token.getType() == TokenType.Identifier) {
            // 读取标识符
            token = tokenReader.read();
            // 当前节点是赋值语句
            node = new SimpleASTNode(ASTNodeType.AssignmentStmt, token.getText());
            token = tokenReader.peek();
            if (token != null && token.getType() == TokenType.Assignment) {
                // 取出等号
                tokenReader.read();
                SimpleASTNode child = additive(tokenReader);
                if (child == null) {
                    throw new Exception("invalid assignment statement.");
                } else {
                    node.addChild(child);
                    token = tokenReader.peek();
                    if (token != null && token.getType() == TokenType.SemiColon) {
                        // 吃掉分号
                        tokenReader.read();
                    } else {
                        throw new Exception("invalid statement");
                    }
                }
            } else {
                // 回溯
                tokenReader.unread();
                node = null;
            }
        }
        return node;
    }

    /**
     * 整型声明
     * @param tokenReader token列表
     * @return 节点
     * @throws Exception 出错信息
     */
    private SimpleASTNode intDeclare(TokenReader tokenReader) throws Exception {
        SimpleASTNode node = null;
        Token token = tokenReader.peek();
        if (token != null && token.getType() == TokenType.Int) {
            // 吃掉int
            tokenReader.read();
            if (tokenReader.peek().getType() == TokenType.Identifier) {
                token = tokenReader.read();
                //创建当前节点，并把变量名记到AST节点的文本值中，这里新建一个变量子节点也是可以的
                node = new SimpleASTNode(ASTNodeType.IntDeclaration, token.getText());
                token = tokenReader.peek();
                if (token != null && token.getType() == TokenType.Assignment) {
                    // 吃掉等号
                    tokenReader.read();
                    SimpleASTNode child = additive(tokenReader);
                    if (child == null) {
                        throw new Exception("invalid variable initialization");
                    } else {
                        node.addChild(child);
                    }
                }
            } else {
                throw new Exception("variable name expected");
            }
            token = tokenReader.peek();
            if (token != null && token.getType() == TokenType.SemiColon) {
                // 吃掉;
                tokenReader.read();
            } else {
                throw new Exception("expecting semicolon");
            }
        }
        return node;
    }

    /**
     * 加法表达式
     * @param tokenReader token列表
     * @return 节点
     * @throws Exception 错误信息
     */
    private SimpleASTNode additive(TokenReader tokenReader) throws Exception {
        SimpleASTNode child1 = multiplicative(tokenReader);
        SimpleASTNode node = child1;
        if (child1 != null) {
            while (true) {
                Token token = tokenReader.peek();
                if (token != null && (token.getType() == TokenType.Plus || token.getType() == TokenType.Minus)) {
                    // 吃掉 +、-号
                    token = tokenReader.read();
                    SimpleASTNode child2 = multiplicative(tokenReader);
                    if (child2 != null) {
                        node = new SimpleASTNode(ASTNodeType.Additive, token.getText());
                        node.addChild(child1);
                        node.addChild(child2);
                    } else {
                        throw new Exception("invalid additive expression");
                    }
                } else {
                    break;
                }
            }
        }
        return node;
    }

    /**
     * 乘法表达式
     * @param tokenReader tokne 列表
     * @return 节点
     * @throws Exception 错误信息
     */
    private SimpleASTNode multiplicative(TokenReader tokenReader) throws Exception {
        SimpleASTNode child1 = primary(tokenReader);
        SimpleASTNode node = child1;

        while (true) {
            Token token = tokenReader.peek();
            if (token != null && (token.getType() == TokenType.Star || token.getType() == TokenType.Slash)) {
                token = tokenReader.read();
                SimpleASTNode child2 = primary(tokenReader);
                if (child2 != null) {
                    node = new SimpleASTNode(ASTNodeType.Multiplicative, token.getText());
                    node.addChild(child1);
                    node.addChild(child2);
                } else {
                    throw new Exception("invalid multiplicative expression");
                }
            } else {
                break;
            }
        }

        return node;
    }

    /**
     * 基础表达式
     * @param tokenReader
     * @return
     * @throws Exception
     */
    private SimpleASTNode primary(TokenReader tokenReader) throws Exception {
        SimpleASTNode node = null;
        Token token = tokenReader.peek();
        if (token != null) {
            if (token.getType() == TokenType.IntLiteral) {
                token = tokenReader.read();
                node = new SimpleASTNode(ASTNodeType.IntLiteral, token.getText());
            } else if (token.getType() == TokenType.Identifier) {
                token = tokenReader.read();
                node = new SimpleASTNode(ASTNodeType.Identifier, token.getText());
            } else if (token.getType() == TokenType.LeftParen) {
                tokenReader.read();
                node = additive(tokenReader);
                if (node != null) {
                    token = tokenReader.peek();
                    if (token != null && token.getType() == TokenType.RightParen) {
                        // 吃掉 ）
                        tokenReader.read();
                    } else {
                        throw new Exception("expecting ')'");
                    }
                } else {
                    throw new Exception("expecting an additive expression inside parenthesis");
                }

            }
        }
        return node;
    }

    /**
     * 打印 AST 树状结构
     * @param node 需要打印的节点
     * @param indent 缩进符，每一级多一个 tab
     */
    void dumpAST(ASTNode node, String indent) {
        System.out.println(indent + node.getType() + " " + node.getText());
        for (ASTNode child : node.getChildren()) {
            dumpAST(child, indent + "\t");
        }
    }

}
